import { Logger } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { MulterModuleAsyncOptions } from "@nestjs/platform-express";
import * as fs from "fs";
import * as multer from "multer";

import { CONFIG_KEY } from "../config/configurations";
import { FileUtils } from "../utils/file.utils";
import { FunctionControlUtils } from "../utils/function-control.utils";
import { PathUtils } from "../utils/path.utils";
import { FileType } from "./file.constant";

// 安全过滤辅助函数
const sanitizeFilename = (name: string) => {
    return name
        .replace(/[^\w\u4e00-\u9fa5-.]/g, "")
        .replace(/\s+/g, "_")
        .replace(/\.{2,}/g, "")
        .substring(0, 100);
};

// 确保上传目录存在，且只执行一次
const ensureUploadDir = FunctionControlUtils.once((dest: string) => {
    const filtTypes: FileType[] = ["image", "video", "audio", "doc", "other"];
    filtTypes.forEach(type => {
        const dir = PathUtils.joinFs(dest, type);
        if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    });
    // console.log("📁 上传目录已创建完毕！");
    Logger.log("📁 上传目录已创建完毕！", "MulterModuleConfig");
});

export const multerModuleRegisterConfig: MulterModuleAsyncOptions = {
    imports: [ConfigModule],
    useFactory: async (configService: ConfigService) => {
        // 此处仅在模块初始化时执行一次。
        // 但模块被其他多个模块导入时，每次获得的都是新实例。因此有多少模块导入注册了 multer 模块，项目启动时此处就会执行多少次。
        const { dest, limits } = configService.get(CONFIG_KEY.upload);
        // 创建上传目录目录
        ensureUploadDir(dest);
        const storage = multer.diskStorage({
            // 每次上传这两个回调函数都会执行
            destination: function (req, file, cb) {
                const category = FileUtils.categorizeFileByMimetype(file.mimetype);
                const fullDest = PathUtils.joinFs(dest, category); // 上传到对应的分类目录
                cb(null, fullDest);
            },
            filename: function (req, file, cb) {
                // 上传的文件名可能是 URI 编码，因此需要解码（非 URI 编码的文件名会保持原样）后存入数据库，并且再对特殊字符进行过滤
                file.originalname = sanitizeFilename(decodeURIComponent(file.originalname));
                const uniqueSuffix = Date.now() + "_" + Math.round(Math.random() * 1e9);
                const category = FileUtils.categorizeFileByMimetype(file.mimetype);
                const mimetype = file.mimetype.split("/").join("_");
                const ext = file.originalname.split(".").pop();
                // // 本地存储时，文件名的组成：文件字段名-唯一标识符(时间戳_随机数)-&文件mime类型(主类型_子类型)&-文件5大分类类型.文件后缀
                // // eg: file-1758539438173_861962059-&video_mp4&-video.mp4
                cb(null, `${file.fieldname}-${uniqueSuffix}-&${mimetype}&-${category}.${ext}`);
            }
        });
        return {
            storage,
            limits
        };
    },
    inject: [ConfigService]
};
